/*
 * Board.java
 *
 * Created on 14 novembre 2003, 23:19
 */

/**
 *
 * @author  liva
 */
public class Board {
    /** Valeurs des cases */
    public final static int PLAYERS=2;
    public final static int BLACK =0;
    public final static int WHITE =1;
    public final static int EMPTY =2;
    public final static int SIDE  =3;
    
    /** Les differentes directions */
    public final static int DIRECTIONS=8;
    public final static int NW =-11;
    public final static int N  =-10;
    public final static int NE =-9;
    public final static int W  =-1;
    public final static int E  =+1;
    public final static int SW = 9;
    public final static int S  =10;
    public final static int SE =11;
    
    /** Les tableaux directions */
    public final static int[] direction={NW, N, NE,
                                          W,     E,
                                         SW, S, SE};
    public final static int TAB_SIZE=100;
    public final static int I_MIN=11;
    public final static int I_MAX=88;
    public final static int SIDE_SIZE=10;
    public final static int COL=8;
    public final static int ROW=8;
    public final static int CORNER_NW=11;
    public final static int CORNER_NE=18;
    public final static int CORNER_SW=81;
    public final static int CORNER_SE=88;
    
    /** Pour recuperer les caracteristiques d'un coup */
    public final static int[] moveShift={0,3,6,9,12,15,18,21,24};
    
    /** Le grand tableau */
    public int[] board;
    
    /** Pieces stables */
    public boolean[][] stable;
    
    /** Le hashValue d'une position 2^64 bits*/
    public long hashValue;
    
    /** Les nombres premiers permettant de calculer
     * la valeur de hachage de la position */
    public long[][] hashNumber;
    
    /** Nombre de pieces pour chacun des joueurs */
    public int[] material;
    public int totalPieces;
    
    /** Poids des cases du plateau pour l'heuristique de position */
    public static final int[] poids = { 
    		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    		0, 100, -20,  10,   5,   5,  10, -20, 100,   0,
    		0, -20, -50,  -2,  -2,  -2,  -2, -50, -20,   0,
    		0,  10,  -2,  -1,  -1,  -1,  -1,  -2,  10,   0,
    		0,   5,  -2,  -1,  -1,  -1,  -1,  -2,   5,   0,
    		0,   5,  -2,  -1,  -1,  -1,  -1,  -2,   5,   0,
    		0,  10,  -2,  -1,  -1,  -1,  -1,  -2,  10,   0,
    		0, -20, -50,  -2,  -2,  -2,  -2, -50, -20,   0,
    		0, 100, -20,  10,   5,   5,  10, -20, 100,   0,
    		0,   0,   0,   0,   0,   0,   0,   0,   0,   0
    };
    
    
    /** Creates a new instance of Board */
    public Board() {
        board=new int[TAB_SIZE];
        material=new int[PLAYERS];
        // Pour la table de transposition
        java.util.Random random=new java.util.Random(System.currentTimeMillis());
        hashNumber=new long[PLAYERS][TAB_SIZE];
        for(int i=I_MIN;i<=I_MAX;i++){
            if(Math.random()<0.5)
                hashNumber[BLACK][i]=java.math.BigInteger.probablePrime(63,random).longValue();
            else
                hashNumber[BLACK][i]=java.math.BigInteger.probablePrime(64,random).longValue();
            if(Math.random()<0.5)
                hashNumber[WHITE][i]=java.math.BigInteger.probablePrime(63,random).longValue();
            else
                hashNumber[WHITE][i]=java.math.BigInteger.probablePrime(64,random).longValue();
        }
        init();
    }
    
     /** Met le plateau a 0 pour le debut de partie*/
    public void init(){
        for(int i=I_MIN;i<TAB_SIZE;i++)
            board[i]=EMPTY;
        for(int i=0;i<SIDE_SIZE;i++){
            board[i]=SIDE;
            board[SIDE_SIZE*(SIDE_SIZE-1)+i]=SIDE;
            board[SIDE_SIZE*i]=SIDE;
            board[SIDE_SIZE*i+(SIDE_SIZE-1)]=SIDE;
        }
        board[44]=board[55]=WHITE;
        board[45]=board[54]=BLACK;
        hashValue=0;
        hashValue^=hashNumber[BLACK][44]^hashNumber[BLACK][55]^hashNumber[WHITE][45]^hashNumber[WHITE][54];
        material[WHITE]=material[BLACK]=2;
        totalPieces=4;
    }
  
    /** Dit si la position proposee pour la couleur
     *  donnee est possible. Renvoie -1 si non et le
     *  coup sous forme de int:
     *  les 24 (8*3) derniers bits contiennent les retournements
     *  dans chacune des directions
     *  les bits 1 a 8 la case a jouer
     */
    public int legal(int player,int position){
        int move=0;
        if(position < I_MIN || position > I_MAX || board[position]!=EMPTY)
            return -1;
        int adv=1-player,nextPos;
        boolean leg=false;
        for(int d=0;d<DIRECTIONS;d++){
            int pos=position;
            int ret=0;
            while(board[nextPos=pos+direction[d]]==adv){
                pos+=direction[d];
                ret++;
            }
            if(ret>0 && board[nextPos]==player){
                leg=true;
                move|=ret<<moveShift[d];
            }
        }
        if(leg){
            move|=position<<moveShift[DIRECTIONS];
        }else{
            move=-1;
        }
        return move;
    }
        
    /** Enregistre le coup specifie */
    public void doMove(int player,int move){
        if(move==-1) return;
        int position = move>>moveShift[DIRECTIONS] & 127; // garde seulement les 6 premiers bits
        int adv=1-player,ret,pos;
        board[position]=player;
        hashValue^=hashNumber[player][position];
        for(int d=0;d<DIRECTIONS;d++){
            pos=position;
            ret=move>>moveShift[d] & 7;
            material[adv]-=ret;
            material[player]+=ret;
            while(ret>0){
                board[pos+=direction[d]]=player;
                hashValue^=hashNumber[player][pos]^hashNumber[adv][pos];
                ret--;
            }
        }
        material[player]++;
        totalPieces++;
    }
    
    /** Annule le coup specifie */
    public void undoMove(int player,int move){
        if(move==-1) return;
        int position=move>>moveShift[DIRECTIONS] & 127;
        int adv=1-player,ret,pos;
        board[position]=EMPTY;
        hashValue^=hashNumber[player][position];
        for(int d=0;d<DIRECTIONS;d++){
            pos=position;
            ret=move>>moveShift[d] & 7;
            material[adv]+=ret;
            material[player]-=ret;
            while(ret>0){
                board[pos+=direction[d]]=adv;
                hashValue^=hashNumber[player][pos]^hashNumber[adv][pos];
                ret--;
            }
        }
        material[player]--;
        totalPieces --;
    }
    
    /** Renvoie true si le joueur concerne peut jouer
     * et false sinon. A mettre ulterieurement dans une
     * classe Player (superclasse de ComputerPlayer)
     */
    public boolean canMove(int player){
        for(int i=I_MIN;i<=I_MAX;i++) if(legal(player,i)!=-1)
            return true;
        return false;       
    }
    
    /** renvoie la difference entre les pions stables pour  le joueur */
    public int stableDiff(int player){
        int adv=1-player;
        stable[player][CORNER_SW]=board[CORNER_SW]==player;
        if(stable[player][CORNER_NW]){
        }else if(stable[adv][CORNER_NW]){
        }
        if(stable[player][CORNER_NE]){
        }else if(stable[adv][CORNER_NE]){
        }
        if(stable[player][CORNER_SW]){
        }else if(stable[adv][CORNER_SW]){
        }
        if(stable[player][CORNER_SE]){
        }else if(stable[adv][CORNER_SE]){
        }
        return 0;
    }
    
    /** Convertit une position entiere sous la forme A2, etc.*/
    public static String decodePosition(int position){
        StringBuffer sb=new StringBuffer();
        sb.append((char)('A'+position%SIDE_SIZE-1));
        sb.append(position/SIDE_SIZE);
        return sb.toString();
    }
    
    /** Convertit une position du type B4 sous la forme d'un entier*/
    public static int encodePosition(String s){
        return SIDE_SIZE*(s.charAt(1)-'0')+s.toUpperCase().charAt(0)-'A'+1;
    }
    
    /** Renvoie true si la partie est finie */
    public boolean gameOver(){
        for(int i=I_MIN;i<=I_MAX;i++) if(legal(BLACK,i)!=-1||legal(WHITE,i)!=-1)
            return false;
        return true;
    }
    /** Affichage du plateau de jeu */
    public String toString(){
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<TAB_SIZE;i++){
            if(i%SIDE_SIZE==0){
                if(i==0)
                    sb.append(' ');
                else if(i/SIDE_SIZE < SIDE_SIZE-1)
                    sb.append(i/SIDE_SIZE);
            } else if(i%SIDE_SIZE==SIDE_SIZE-1){
                if(i/SIDE_SIZE==4)
                    sb.append("\t* Noirs = " + material[BLACK]);
                if(i/SIDE_SIZE==5)
                    sb.append("\to Blancs = " + material[WHITE]);
                sb.append("\n");
            }else if(i/SIDE_SIZE==0){
                sb.append(" " + (char)('A'+i-1));
            } else if (i/SIDE_SIZE < SIDE_SIZE-1){
                if(board[i]==BLACK)
                    sb.append(" " + '*');
                else if(board[i]==WHITE)
                    sb.append(" " + 'o');
                else
                    sb.append(" " + '-');
            }
        }    
        return sb.toString();    
    }
    
    public static void main(String[] args){
        Board board=new Board();
        int move=0;
        for(int i=I_MAX;i>=I_MIN;i--) if((move=board.legal(BLACK,i))!=-1){
            board.doMove(BLACK,move);
            System.out.println(decodePosition(i));
            break;
        }
        System.out.println(board);
        board.undoMove(BLACK,move);
        System.out.println(board);
    }
}
